﻿using System;
using System.Linq;
using CoreLibrary;
using CoreLibrary.Paging;
using Entities.DataBase;
using Entities.DataBase.Obj;
using Entities.DataBase.Persistent;
using Entities.Enumeration;
using WebService.Email;
using WebService.Service.Transfer;

namespace WebService.Service {

    public class ProductServiceX : AbstractService, IProductServiceX {

        public bool AddProduct(string passport, AddProductRequest request) {
            return Run(passport, token => {
                Locate<IProductRepository>().Add(new Product {
                    CatalogId   = request.CatalogId,
                    Weight      = request.Weight,
                    Postage     = (decimal)request.Price,
                    WeightUnit  = Util.GetString(request.WeightUnit),
                    CName       = Util.GetString(request.CName),
                    Name        = Util.GetString(request.Name),
                    Description = Util.GetString(request.Description),
                    Keywords    = Util.GetString(request.Keywords),
                    CreatorId   = token.UserId
                });
                return true;
            });
        }

        public bool DeleteProduct(string passport, string productId) {
            return Run(passport, token => {
                Locate<IProductRepository>().Remove(productId);
                return true;
            });
        }

        public bool UpdateProduct(string passport, string productId, UpdateProductRequest request) {
            return Run(passport, token => {
                Locate<IProductRepository>().Update(productId,
                    new ProductUpdator {
                        Name        = Util.GetString(request.Name),
                        Cname       = Util.GetString(request.CName),
                        Description = Util.GetString(request.Description),
                        Keywords    = Util.GetString(request.Keywords),
                        Weight      = request.Weight,
                        WeightUnit  = request.WeightUnit,
                        Postage     = request.Price,
                        ListOrder   = request.ListOrder,
                    }
                );
                return true;
            });
        }

        public PagingResult<ProductInfo> ListProducts(string passport, int catalogId, PagingRequest request) {
            return Run(passport, token => {
                var products = Services.Locate<IProductRepository>().List(catalogId, 0, 1000);
                return new PagingResult<ProductInfo> {
                    Offset = request.Offset,
                    Total  = products.Count,
                    Data   = products.Skip(request.Offset).Take(request.Limit).Select(Convert).OrderBy(p => p.ListOrder).ToList()
                };
            });
        }

        public PagingResult<ProductInfo> ListProductsByKeyword(string passport, PagingRequest request) {
            return Run(passport, token => {
                var products = Services.Locate<IProductRepository>().List(request.Keyword);
                return new PagingResult<ProductInfo> {
                    Offset = request.Offset,
                    Total  = products.Count,
                    Data   = products.Skip(request.Offset)
                                     .Take(request.Limit)
                                     .OrderBy(p => p.CName)
                                     .Select(Convert)
                                     .OrderBy(p => p.ListOrder)
                                     .ToList()
                };
            });
        }

        public PagingResult<ProductInfo> SearchProducts(string passport, PagingRequest request) {
            return Run(passport, token => {
                var products = Locate<IProductRepository>().List(request.Keyword);
                return new PagingResult<ProductInfo> {
                    Offset = request.Offset,
                    Total  = products.Count,
                    Data   = products.Skip(request.Offset)
                                     .Take(request.Limit)
                                     .Select(Convert)
                                     .ToList()
                };
            });
        }

        public string CreatePurchaseRecord(string passport, CreatePurchaseRecordRequest request) {
            return Run(passport, token => {
                var recordId   = GuidUtil.GuidN();
                var operatorId = Util.GetString(request.BuyerId, token.UserId);

                Locate<IPurchaseRecordRepository>().CreateRecord(new PurchaseRecord {
                    Id             = recordId,
                    PostOperatorId = Util.GetString(request.PosterId, operatorId),
                    OperatorId     = operatorId,
                    ProductId      = request.ProductId,
                    Cost           = (decimal)request.Cost,
                    Postage        = (decimal)request.Postage,
                    CostCurrency   = request.CostCurrency,
                    Count          = request.Count,
                    Location       = request.Location,
                    CurrencyRate   = (decimal) request.CurrencyRate,
                    DateCreated    = request.DatePurchased ?? DateTime.UtcNow
                });

                EmailManager.SendEmail(new CreatePurchaseRecordTemplate {
                    Request    = request,
                    OperatorId = token.UserId
                });

                return recordId;

            });
        }

        public bool DeletePurchaseRecord(string passport, string recordId) {
            return Run(passport, token => {
                var record = Locate<IPurchaseRecordRepository>().GetRecord(recordId);
                Locate<IPackageRepository>().RemoveByReference(recordId);
                Locate<IPurchaseRecordRepository>().RemoveRecord(recordId);

                SyncStock(passport, record.ProductId, record.Location);
                EmailManager.SendEmail("删除购买记录", token.FirstName + "刚刚删除了一条购买产品记录");
                return true;
            });
        }

        public string TransferPurchaseRecord(string passport, TransferPurchaseRecordRequest request) {
            return Run(passport, token => {
                var record   = Locate<IPurchaseRecordRepository>().GetRecord(request.Id);
                var recordId = Locate<IPurchaseRecordRepository>().TransferRecord(
                    new TransferRecordReq {
                        OperatorId     = request.OperatorId,
                        RecordId       = request.Id,
                        Count          = request.Count,
                        Postage        = request.Postage,
                        TargetLocation = request.Location,
                    }
                );

                SyncStock(passport, record.ProductId, record.Location);
                SyncStock(passport, record.ProductId, request.Location);

                EmailManager.SendEmail("转移库存记录", token.FirstName + "刚刚转移了一条库存购买记录");
                return recordId;
            });
        }

        public bool UpdatePurchaseRecord(string passport, UpdatePurchaseRecordRequest request) {
            return Run(passport, token => {
                var record = Locate<IPurchaseRecordRepository>().GetRecord(request.Id);
                Locate<IPurchaseRecordRepository>().UpdateRecord(request.Id, new PurchaseRecordUpdator {
                    Id             = request.Id,
                    PostOperatorId = request.PostOperatorId,
                    OperatorId     = request.OperatorId,
                    DatePurchased  = request.DatePurchased,
                    Count          = request.Count,
                    Cost           = request.Cost,
                    Postage        = request.Postage,
                    Location       = request.Location,
                    Rate           = request.Rate
                });

                SyncStock(passport, record.ProductId, record.Location);
                EmailManager.SendEmail("更新购买记录", token.FirstName + "刚刚更新一条库存购买记录");
                return true;
            });
        }

        public PagingResult<PurchaseRecordInfo> ListPurchaseRecords(string passport, string productId, PagingRequest request) {
            return Run(passport, token => {
                var records = Locate<IPurchaseRecordRepository>().ListRecordsByProductId(productId);
                return new PagingResult<PurchaseRecordInfo> {
                    Offset = request.Offset,
                    Total  = records.Count,
                    Data   = records.OrderBy(s => s.ProductName)
                                    .Skip(request.Offset)
                                    .Take(request.Limit)
                                    .ToList()
                };
            });
        }

        public PagingResult<PurchaseRecordInfo> SearchPurchaseRecords(string passport, SearchPurchaseRecordRequest request) {
            return Run(passport, token => {
                var records = request.Location == null
                            ? Locate<IPurchaseRecordRepository>().ListAllRecords()
                            : Locate<IPurchaseRecordRepository>().ListRecordsByLocation((int)request.Location);

                var name = Util.GetString(request.ProductName);
                return new PagingResult<PurchaseRecordInfo> {
                    Offset = request.Offset,
                    Total  = records.Count,
                    Data   = records.Where(s => string.Empty.Equals(name) || s.ProductName.Equals(name) || s.Name.Equals(name))
                                    .Where(s => request.Price == null || s.Cost == (decimal)request.Price)
                                    .Where(s => request.PurchaseDate == null || s.DateCreated.Date.CompareTo(((DateTime)request.PurchaseDate).Date) == 0)
                                    .Where(s => string.Empty.Equals(Util.GetString(request.OperatorId)) || s.OperatorId.Equals(request.OperatorId))
                                    .Where(s => string.Empty.Equals(Util.GetString(request.PostOperatorId)) || s.PostOperatorId.Equals(request.PostOperatorId))
                                    .Skip(request.Offset)
                                    .Take(request.Limit)
                                    .OrderByDescending(r => r.DateCreated)
                                    .ToList()
                };
            });
        }

        public PagingResult<StockInfo> ListStocks(string passport, PagingRequest request) {
            return Run(passport, token => {
                var stocks = Locate<IProductStockRepository>().ListAllStocks();
                return new PagingResult<StockInfo> {
                    Offset = request.Offset,
                    Total = stocks.Count,
                    Data = stocks.Skip(request.Offset)
                                   .Take(request.Limit)
                                   .ToList()
                };
            });
        }

        public PagingResult<StockInfo> SearchStocks(string passport, SearchStockRequest request) {
            return Run(passport, token => {
                var stocks = Locate<IProductStockRepository>().ListAllStocks();
                return new PagingResult<StockInfo> {
                    Offset = request.Offset,
                    Total  = stocks.Count,
                    Data   = stocks.Where(s => request.Location == null || s.Location == request.Location)
                                   .Where(s => string.IsNullOrEmpty(request.ProductId) || s.ProductId.Equals(request.ProductId))
                                   .Skip(request.Offset)
                                   .Take(request.Limit)
                                   .OrderBy(s => s.ProductName)
                                   .ToList()
                };
            });
        }

        public PagingResult<StockInfo> ListStocksByLocation(string passport, int location, PagingRequest request) {
            return Run(passport, token => {
                var stocks = Locate<IProductStockRepository>().ListStocksByLocation(location);
                return new PagingResult<StockInfo> {
                    Offset = request.Offset,
                    Total  = stocks.Count,
                    Data   = stocks.Skip(request.Offset)
                                   .Take(request.Limit)
                                   .ToList()
                };
            });
        }

        public bool SyncStock(string passport, string productId, int location) {
            return Run(passport, token => {
                var stock = Locate<IProductStockRepository>().GetStock(productId, location);
                if (stock == null) {
                    return true;
                }

                var orderRecordSummary    = Locate<IOrderRecordRepository>().GetRecordSummary(stock.Id);
                var purchaseRecordSummary = Locate<IPurchaseRecordRepository>().GetRecordSummary(productId, location);
                if (purchaseRecordSummary == null || purchaseRecordSummary.TotalNumOfPurchases == 0) {
                    Locate<IProductStockRepository>().SyncStock(new SyncStockReq {
                        Postage   = 0,
                        ProductId = productId,
                        Location  = location,
                        Cost      = 0,
                        Count     = 0,
                    });
                    return true;
                }

                var averagePostage = (purchaseRecordSummary.TotalPostage) / (purchaseRecordSummary.TotalNumOfPurchases);
                var averageCost    = (purchaseRecordSummary.TotalCost)    / (purchaseRecordSummary.TotalNumOfPurchases);
                Locate<IProductStockRepository>().SyncStock(new SyncStockReq {
                    ProductId = productId,
                    Location  = location,
                    Postage   = MathUtil.Round(averagePostage, 3),
                    Cost      = MathUtil.Round(averageCost,    3),
                    Count     = Math.Max(purchaseRecordSummary.TotalNumOfPurchases - orderRecordSummary.TotalNumOfOrders, 0),
                });

                return true;
            });
        }

        private static ProductInfo Convert(Product product) {
            return new ProductInfo {
                ListOrder   = product.ListOrder,
                ProductId   = product.ProductId,
                CName       = product.CName,
                CatalogId   = product.CatalogId,
                Description = product.Description,
                Keywords    = product.Keywords,
                Name        = product.Name,
                Postage     = (double)product.Postage,
                Weight      = product.Weight,
                WeightUnit  = product.WeightUnit,

            };
        }
        
    }
}
